package domain;
import domain.exceptions.*;
import domain.Game.action;
import java.util.Iterator;
import java.util.Vector;
import presentation.Observer;
/**
 *
 * @author Shai Givony
 */

public abstract class Agent implements Cloneable{

	// health conditions:
	public static final int HEALTHY		= 100;
	public static final int DEAD		= 0;


	private String     		_name;
    private action  		_lastAction;
    private String 			_assignment;
    private Square     		_currentSquare;
    private Square     		_previousSquare;
    private int        		_score;
    private Vector<Observer> _observers = new Vector<Observer>();
	private boolean 		_lastSquareWasIce;	// for gui
	private int				_health;
	private boolean 		_flagLatelyTaken;
	private int				_flagsTakenByMeValue;
	private Agent			_enemy;
	private int				_arsenal;

	public Agent(String name, action lastAction, String assignment) {
        this._name          		= name;
        this._lastAction 			= lastAction;
        this._assignment			= assignment;
        this._currentSquare        	= null;
        this._previousSquare 		= null;
        this._score         		= Game.INITIALE_SCORE;
        this._observers     		= new Vector<Observer>();
        this._lastSquareWasIce		= false;
        this._health				= Agent.HEALTHY;
        this._flagLatelyTaken 		= false;
        this._flagsTakenByMeValue 	= 0;
        this._enemy					= null;
        this._arsenal 				= 0;
    }

    //Copy Constructor
    public Agent(Agent other){
    	this._name 					= new String(other.getName());
    	this._lastAction 			= other.getLastAction();
    	this._assignment 			= other._assignment;
    	this._currentSquare 		= other._currentSquare;		// this is not right, we know, but it's fixed later (this square is overriden by the true square)
    	this._previousSquare	    = other._previousSquare;
    	this._score 				= other.getScore();
    	this._observers 			= null;
    	this._lastSquareWasIce 		= other._lastSquareWasIce;
    	this._health 				= other._health;
    	this._flagLatelyTaken 		= other._flagLatelyTaken;
    	this._flagsTakenByMeValue 	= other._flagsTakenByMeValue;
    	this._enemy					= null;
    	this._arsenal 				= other._arsenal;
    }



	public String getName() {
        return _name;
    }

    public Square getSquare() {
        return _currentSquare;
    }

    public Game.action getLastAction() {
    	return _lastAction;
    }

    public void setLastAction(action lastAction) {
    	this._lastAction = lastAction;
    }

    private boolean returnTrueInProbability(double probability){
    	double randomNumber = Math.random();
    	return randomNumber < probability;
    }


    public void act(action act, Board board, Vector<Agent> agents) throws CannotMoveThereException{

    	boolean whileThinking = board != Game._board;

    	//this._flagLatelyTaken = false;
    	if (!isDead()){
    		Debug.println("action: " + act + " ");

    		// checking if action is shoot:
    		if (act == action.SHOOT){
				shoot(agents, board);
    		}

    		// checking if agent is on icy square:
    		boolean currentSquareIsIce = isStandingOnIcy();
    		boolean ignoringIce		   = is_ignoringIce(whileThinking);

    		if (whileThinking && currentSquareIsIce && !ignoringIce){
    				act = this._lastAction;		// means: given act is ignored, and act is actually becomes last action.
    		}
    		if (!whileThinking && currentSquareIsIce && !ignoringIce){
    			boolean iMustNotIgnoreIce = returnTrueInProbability(1-Game.getProbability());
    			if (iMustNotIgnoreIce)
					act = this._lastAction;	// means: given act is ignored, and act is actually becomes last action.
    		}

    		/*for the gui*/
    		if (currentSquareIsIce){
    			this._lastSquareWasIce = true;	// for gui
    		}else{
    			this._lastSquareWasIce = false;	// for gui
    		}

    		move(act, board);

    		Debug.println();

    		if (!whileThinking)
    			updateGui(0);
    	}	// end of if !isDead()
    	else{
    		Debug.println("I'm dead meat.");
    	}
    }

	public void move(action act, Board board) throws CannotMoveThereException {

		boolean whileThinking = board != Game._board;
		Square candidateSquare;

		// updates last action:
		this._lastAction = act;


		// checking if action is UP\RIGHT\DOWN\LEFT:
		if (!isDead()){
			if (act != action.SHOOT){
				try{
					candidateSquare = _currentSquare.neighbour(act, board);
					board.placeAgent(this, candidateSquare);
					Debug.println("succeeded");
					try {
						_previousSquare.removeAgent();
					} catch (Exception e) {
						System.err.println(e.getMessage());
					}

					// checking if agent reached a flagged Square:
					Square myNewSquare = getSquare();
					if (myNewSquare.isContainFlag()){
						try {
							takeFlag(myNewSquare, board);
						} catch (TryingToRemoveNonExistingFlagException e) {
							e.printStackTrace();
						}
					}

				} catch (CannotPlaceAgentHereException ex) {
					setPreviousSquare(_currentSquare);
					throw new CannotMoveThereException("Cannot move from "+ getSquare()+ " " + act + " (1)");
				} catch (NoSuchSquareException e){
					setPreviousSquare(_currentSquare);
					throw new CannotMoveThereException("Cannot move from "+ getSquare()+ " " + act + " (2)");
				} finally {
					if (!whileThinking){
						Debug.println("### " + this.toString() + " Position after move: " + _currentSquare + " ###\n");      //del
						Debug.println("previous square: "+_previousSquare.toStringLocation());
						Debug.println("current  square: "+_currentSquare.toStringLocation());
					}
				}
			}
		}
	}

	private boolean isArsenalEmpty() {
		return this._arsenal == 0;
	}

	public void shoot(Vector<Agent> agents, Board board){
		if (!isArsenalEmpty()){
			this._arsenal--;
			Vector<Agent> agentsToShoot = scan(agents);
			if (agentsToShoot.isEmpty()){
				Debug.println(this._name + ": \"No one to shoot.\"");
			}
			else{
				for (Iterator<Agent> it = agentsToShoot.iterator(); it.hasNext();) {
					Agent victim = it.next();
					Debug.print(this._name + " BANG BANG ---> " + victim.getName());
					victim.die(board);//TODO: check here if we do not miss the first agent.
				}
				Debug.println();
			}
		}
		else{
			Debug.println(this._name + ": Can't shoot, not enough bullets");
		}
	}

    private Vector<Agent> scan(Vector<Agent> agents) {

        //Vector<Agent> agents = Game.getAgents();
        Vector<Agent> agentsToKill = new Vector<Agent>();
        for (Iterator<Agent> it = agents.iterator(); it.hasNext();) {
            Agent agent = it.next();
            if( agent != this && inSight(agent) ){
                agentsToKill.add(agent);
            }
        }//end of for
        return agentsToKill;
    }

    /**
     *
     * @param agent to shoot
     * @return true iff there is a clean shot between this and the agent
     */
    private boolean inSight(Agent agent) {
        try {
        	//using inSight in the board, that gets as a parameter two squares and return
        	//true iff there is a they are parallel and have no obsticle between them.
            return Game._board.inSight(this.getSquare(), agent.getSquare());
        } catch (MultipleAgentsHaveTheSameLocation ex) {
            System.err.println(ex.getMessage());
        }
        return false;//TODO: CHECK IF NOT TO CHANGE
    }

	private boolean isStandingOnIcy() {
		return this._currentSquare instanceof SquareIce;
	}

    public void takeFlag(Square square, Board board) throws TryingToRemoveNonExistingFlagException{
    	boolean whileThinking = Game._board != board;

    	// updates agent's score:
        Game.increaseScoreBy(square.getFlag().getValue(), this, this._enemy);
    	this._flagsTakenByMeValue += square.getFlag().getValue();
        //setScore(getScore() + square.getFlag().getValue());



        // updates board:
        try{
            board.removeFlag(square);
        } catch (TryingToRemoveNonExistingFlagException ex){
            throw new TryingToRemoveNonExistingFlagException();
        }
        this._flagLatelyTaken = true;
		// updates GUI:
        if (!whileThinking)
        	updateGui(1);
    }

    public void initAgentPlaceOnBoard(Square square) {
        setSquare(square);
        try {
            Game._board.placeAgent(this, square);
        } catch (CannotPlaceAgentHereException ex) {
            System.err.println(ex.getMessage());
        }
        int cell = square.getColumn() + 1 + ( square.getRow() * Game._board.getNumberOfColumns());
        updateGui(cell);
    }

    //public abstract void die(Board board);
    public void die(Board board){
		boolean whileThinking = board != Game._board;
		setHealth(Agent.DEAD);
		Debug.println(this._name + ": I'm dead");
		if (!whileThinking)
			updateGui(2);
    }

    public int getScore(){
        return this._score;
    }

    public abstract void play(Board board, Vector<Agent> agents, int steps);

    public void setSquare(Square square) {
    	//this._previousSquare = this._currentSquare;
        this._currentSquare = square;
    }

    public void setScore(int score) {
        this._score = score;
    }

    public void subscribe(Observer observer){
        this._observers.addElement(observer);
    }

    protected void updateGui(int status) {
        for (Iterator<Observer> it = _observers.iterator(); it.hasNext();) {
            Observer observer = it.next();
            observer.updateGui(this, status);
        }
    }

    public String toString(){
        return "{Agent: "+getName() + ", Square: "+getSquare()+ ", Score: "+getScore()+"}\n";
    }

//    @Override
//    public Object clone(){
//    	Debug.println("Agent CLONED");
//    	try {
//			return super.clone();
//		} catch (CloneNotSupportedException e) {
//			e.printStackTrace();
//		}
//		return null;
//    }//end of clone

    @Override
    public boolean equals(Object other){
    	if(!(other instanceof Agent)){
    		return false;
    	}
    	else{
    		Agent otherAgent = (Agent)other;
    		return this.getName().equalsIgnoreCase(otherAgent.getName()) &&
    			   this.getSquare().equals(otherAgent.getSquare()) &&
    			   this.isDead() == otherAgent.isDead() &&
    			   this._health == otherAgent._health &&
    			   this._score == otherAgent._score &&
    			   this._lastAction == otherAgent._lastAction;
    	}
    }//end of equals

	public boolean isComingFromIce() {
		return this._lastSquareWasIce;
	}

	public void setHealth(int healthCondition){
		this._health = healthCondition;
	}

	public boolean isDead(){
		return this._health == Agent.DEAD;
	}

	public void setFlagLatelyTaken(boolean _flagLatelyTaken) {
		this._flagLatelyTaken = _flagLatelyTaken;
	}

	public boolean isFlagLatelyTaken() {
		return _flagLatelyTaken;
	}

	public int get_flagsTakenByMeValue() {
		return _flagsTakenByMeValue;
	}

	protected void setAssignment(String _assignment) {
		this._assignment = _assignment;
	}

	protected String getAssignment() {
		return _assignment;
	}

	public void setEnemy(Agent _enemy) {
		this._enemy = _enemy;
	}

	public Agent getEnemy() {
		return _enemy;
	}

	protected void set_arsenal(int _arsenal) {
		this._arsenal = _arsenal;
	}

	public int get_arsenal() {
		return _arsenal;
	}

	/*public boolean isChoiceMessage() {
		//if (this.choiceAgentMessage = true)
		return this.choiceAgentMessage ;
	}*/


	public boolean is_ignoringIce() {
		return (_currentSquare instanceof SquareIce) && _previousSquare ==_currentSquare;
		//return _currentSquare instanceof SquareIce && !_firstTime;
	}
	public boolean is_ignoringIce(boolean thinking) {
		if (!thinking) {
			//Debug.println("previous square: "+_previousSquare.toStringLocation());
			//Debug.println("current  square: "+_currentSquare.toStringLocation());
		}
		return (_currentSquare instanceof SquareIce) && _previousSquare ==_currentSquare;
		//return _currentSquare instanceof SquareIce && !_firstTime;
	}

	public void setPreviousSquare(Square previousSquare) {
		this._previousSquare = previousSquare;
	}

	public Square getPreviousSquare() {
		return this._previousSquare;
	}
}
